package com.zhn.evsi.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhn.evsi.client.OAuthClient;
import com.zhn.evsi.common.RedisCache;
import com.zhn.evsi.domain.dto.evsi.EvsiTokenDTO;
import com.zhn.evsi.domain.dto.evsi.EvsiVerifyDTO;
import com.zhn.evsi.domain.entity.evsi.EvsiAuthAccount;
import com.zhn.evsi.domain.entity.evsi.EvsiAuthToken;
import com.zhn.evsi.exception.CustomException;
import com.zhn.evsi.mapper.EvsiAuthTokenMapper;
import com.zhn.evsi.service.IEvsiAuthAccountService;
import com.zhn.evsi.service.IEvsiAuthTokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class EvsiAuthTokenServiceImpl extends ServiceImpl<EvsiAuthTokenMapper, EvsiAuthToken> implements IEvsiAuthTokenService {

    @Resource
    private OAuthClient oAuthClient;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IEvsiAuthAccountService authAccountService;

    /**
     * 返回授权地址
     * */
    @Override
    public String getUrl() {

        Map<String, String> map = redisMap();

        return StrUtil.format("{}/oauth/authorize?response_type={}&redirect_uri={}&client_id={}&scope={}&state={}",
                map.get("oauthUrl"), "code", URLUtil.encode(map.get("redirectUrl")), URLUtil.encode(map.get("clientId")), map.get("scopes"), map.get("state"));
    }

    /**
     * 查询授权信息
     * */
    @Override
    public List<EvsiAuthToken> oauthTokenList(String mail) {

        if (ObjectUtil.isNotEmpty(mail)) {
            return this.list(new LambdaQueryWrapper<EvsiAuthToken>().eq(EvsiAuthToken::getEmail, mail));
        }else {
            return this.list();
        }
    }


    /**
     * 回调获取code，并发送请求，存放到redis，并持久化保存
     * @param code - 返回的code
     * */
    @Override
    public void oauthToken(String code, String email) {

        EvsiAuthToken evsiAuthToken = new EvsiAuthToken();
        // base64 加密密钥
        String authorizationCode = authorizationBase64();
        // 获取token与角色信息
        JSONObject token = oAuthClient.oauthToken(authorizationCode, "authorization_code", code);
        EvsiTokenDTO evsiTokenDTO = JSON.toJavaObject(token, EvsiTokenDTO.class);
        if (evsiTokenDTO == null
                || ObjectUtil.equal("invalid_request", evsiTokenDTO.getError())
                || ObjectUtil.equal("Authorization code not found", evsiTokenDTO.getError_description())){
            throw new CustomException("当前code过期，请重新授权！");
        }

        JSONObject verify = oAuthClient.verify(StrUtil.format("{} {}", evsiTokenDTO.getToken_type(), evsiTokenDTO.getAccess_token()));
        EvsiVerifyDTO evsiVerifyDTO = JSON.toJavaObject(verify, EvsiVerifyDTO.class);
        if (verify == null
                || ObjectUtil.equal("invalid_request", evsiVerifyDTO.getError())
                || ObjectUtil.equal("Authorization code not found", evsiVerifyDTO.getError_description())){
            throw new CustomException("获取当前角色失败，请重新授权！");
        }

        EvsiAuthToken oEvsiAuthToken = evsiAuthToken.splicingToken(evsiTokenDTO, evsiVerifyDTO);

        // redis存储当前token20min，并进行持久化存储
        redisCache.setCacheObject("User:accessToken:" + email + ":" + oEvsiAuthToken.getCharacterID(), oEvsiAuthToken.getAccessToken(), 20, TimeUnit.MINUTES);
        oEvsiAuthToken.setEmail(email);
        // 判断表单中是否存在这个角色，是则更新，没有则添加
        if (null != this.getOne(new LambdaQueryWrapper<EvsiAuthToken>().eq(EvsiAuthToken::getCharacterID, oEvsiAuthToken.getCharacterID()))) {
            this.update(new LambdaUpdateWrapper<EvsiAuthToken>().set(EvsiAuthToken::getAccessToken, oEvsiAuthToken.getAccessToken()).eq(EvsiAuthToken::getCharacterID, oEvsiAuthToken.getCharacterID()));
            log.info("角色{}已存在，完成更新！", oEvsiAuthToken.getCharacterName());
        } else {
            this.save(oEvsiAuthToken);
        }
    }

    /**
     * 通过refresh_token获取当前角色的token
     * */
    public String oauthTokenV2(Integer characterId){

        // 获取当前登录用户的email
        String email = "1085155177@qq.com";
        // 从redis获取当前角色的token TODO 从security中获取当前登录用户的邮箱，然后accessToken:当前登录用户邮箱:角色名称查询
        String token = "";
        token = redisCache.getCacheObject("User:accessToken:" + email + ":" + characterId);
        if (StrUtil.isEmpty(token)) {
            // base64 加密密钥
            String authorizationCode = authorizationBase64();
            // 通过角色id查询出来当前角色信息
            EvsiAuthToken evsiAuthToken = this.getOne(new LambdaQueryWrapper<EvsiAuthToken>().eq(EvsiAuthToken::getCharacterID, characterId));
            // 通过refresh_token查询当前角色的token
            JSONObject oauthTokenV2 = oAuthClient.oauthTokenV2(authorizationCode, "refresh_token", evsiAuthToken.getRefreshToken());
            EvsiTokenDTO evsiTokenDTO = JSON.toJavaObject(oauthTokenV2, EvsiTokenDTO.class);
            token = evsiTokenDTO.getAccess_token();
            // 存入redis
            redisCache.setCacheObject("User:accessToken:" + email + ":" + characterId, token, evsiTokenDTO.getExpires_in(), TimeUnit.SECONDS);
            // 持久化更新
            this.update(new LambdaUpdateWrapper<EvsiAuthToken>().set(EvsiAuthToken::getAccessToken, token).eq(EvsiAuthToken::getCharacterID, characterId));
        }
        return token;
    }


    /**
     * 生成密钥
     * */
    private String authorizationBase64(){

        Map<String, String> map = redisMap();
        // base64 加密密钥
        String authorizationCode = StrUtil.format("{}:{}", map.get("clientId"), map.get("secretKey"));
        return "Basic " + Base64.encode(authorizationCode);
    }

    /**
     * stream流提取参数
     * */
    private Map<String, String> redisMap() {

        Map<String, String> map = redisCache.getCacheObject("account:eve:");

        if (ObjectUtil.isEmpty(map)) {
            map = new HashMap<>();

            List<EvsiAuthAccount> authAccountList = authAccountService.list(new LambdaQueryWrapper<EvsiAuthAccount>().eq(EvsiAuthAccount::getGroupName, "eve"));

            if (ObjectUtil.isEmpty(authAccountList)) {
                throw new CustomException("授权参数异常！");
            }

            map.put("oauthUrl", authAccountList.stream().filter(v -> ObjectUtil.equal(v.getAccount(), "oauth_url")).collect(Collectors.toList()).get(0).getParameter());
            map.put("redirectUrl", authAccountList.stream().filter(v -> ObjectUtil.equal(v.getAccount(), "redirect_url")).collect(Collectors.toList()).get(0).getParameter());
            map.put("clientId", authAccountList.stream().filter(v -> ObjectUtil.equal(v.getAccount(), "client_id")).collect(Collectors.toList()).get(0).getParameter());
            map.put("secretKey", authAccountList.stream().filter(v -> ObjectUtil.equal(v.getAccount(), "secret_key")).collect(Collectors.toList()).get(0).getParameter());
            map.put("scopes", authAccountList.stream().filter(v -> ObjectUtil.equal(v.getAccount(), "scopes")).collect(Collectors.toList()).get(0).getParameter());
            map.put("state", authAccountList.stream().filter(v -> ObjectUtil.equal(v.getAccount(), "state")).collect(Collectors.toList()).get(0).getParameter());

            redisCache.setCacheObject("account:eve:", map, 1, TimeUnit.DAYS);
        }
        return map;
    }

}
